package secondary

/*
	请实现一个函数用来判断字符串是否表示数值（包括整数和小数）。

	数值（按顺序）可以分成以下几个部分：

		1. 若干空格
		2. 一个小数或者整数
		3. （可选）一个'e'或'E'，后面跟着一个整数
		4. 若干空格
	小数（按顺序）可以分成以下几个部分：

		1. （可选）一个符号字符（'+' 或 '-'）
		2. 下述格式之一：
			1. 至少一位数字，后面跟着一个点 '.'
			2. 至少一位数字，后面跟着一个点 '.' ，后面再跟着至少一位数字
			3. 一个点 '.' ，后面跟着至少一位数字
		3. 整数（按顺序）可以分成以下几个部分：

			1. （可选）一个符号字符（'+' 或 '-'）
			2. 至少一位数字
	部分数值列举如下：

		- ["+100", "5e2", "-123", "3.1416", "-1E-16", "0123"]
	部分非数值列举如下：

		- ["12e", "1a3.14", "1.2.3", "+-5", "12e+5.4"]
	示例 1：

	输入：s = "0"
	输出：true
	示例 2：

	输入：s = "e"
	输出：false
	示例 3：

	输入：s = "."
	输出：false
	示例 4：

	输入：s = ".1"
	输出：true

	提示：

	1 <= s.length <= 20
	s 仅含英文字母（大写和小写），数字（0-9），加号 '+' ，减号 '-' ，空格 ' ' 或者点 '.' 。

	来源：力扣（LeetCode）
	链接：https://leetcode-cn.com/problems/biao-shi-shu-zhi-de-zi-fu-chuan-lcof
	著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
*/
func IsNumber(s string) bool {
	return isNumber(s)
}

/*
	数值的表现形式:
	1. 包含整数部分(中括号表示可以没有)
		A[.[B]][E|eC]
	2. 不包含整数部分
		.B[E|eC]
*/
func isNumber(s string) bool {
	for i := 0; i < len(s); i++ {
		if s[i] != ' ' {
			s = s[i:]
			break
		}
	}
	for i := len(s) - 1; i >= 0; i-- {
		if s[i] != ' ' {
			s = s[:i+1]
			break
		}
	}
	numeric := false
	index := 0
	numeric = scanInteger(s, &index)
	if index == len(s) {
		return numeric
	}
	if s[index] == '.' {
		index++
		numeric = scanUnsignedInteger(s, &index) || numeric
	}
	if index == len(s) {
		return numeric
	}
	if s[index] == 'E' || s[index] == 'e' {
		index++
		numeric = scanInteger(s, &index) && numeric
	}
	return numeric && index == len(s)
}

func scanUnsignedInteger(s string, index *int) bool {
	before := *index
	for ; *index < len(s); *index++ {
		if s[*index] < '0' || s[*index] > '9' {
			break
		}
	}
	return *index > before
}

func scanInteger(s string, index *int) bool {
	if *index <= len(s)-1 && (s[*index] == '+' || s[*index] == '-') {
		*index++
	}
	return scanUnsignedInteger(s, index)
}
